﻿using K9Nano.Domain.Caching;
using K9Nano.Domain.Entities;
using K9Nano.Domain.Organization;
using K9Nano.Exceptions;
using K9Nano.GlobalOptions;
using Microsoft.EntityFrameworkCore;

namespace K9Nano.Domain.Authentication;

public class UserManager<TUser, TRole, TUserRole, TDbContext>(TDbContext db, IUserCache cache) : IUserManager<TUser, TRole>
    where TUser : UserBase
    where TRole : RoleBase
    where TUserRole : UserRoleBase<TUser, TRole>, new()
    where TDbContext : DbContext
{
    public virtual async Task<TUser> CreateAsync(TUser user, CancellationToken cancellation)
    {
        user.IsEnabled = true;
        user.SetPassword(user.Password);
        db.Add(user);
        await db.SaveChangesAsync(cancellation);
        return user;
    }

    public virtual async Task<TUser> UpdateAsync(TUser user, CancellationToken cancellation)
    {
        if (await db.SaveChangesAsync(cancellation) > 0)
        {
            cache.Remove(user.Id);
        }
        return user;
    }

    public virtual async Task AddRolesAsync(TUser user, IEnumerable<TRole> roles, CancellationToken cancellation)
    {
        var userRoles = roles.Select(a => new TUserRole
        {
            UserId = user.Id,
            RoleId = a.Id
        });
        db.AddRange(userRoles);
        if (await db.SaveChangesAsync(cancellation) > 0)
        {
            cache.Remove(user.Id);
        }
    }

    public virtual async Task RemoveRolesAsync(TUser user, IEnumerable<TRole> roles, CancellationToken cancellation)
    {
        var roleIds = roles.Select(a => a.Id).ToArray();
        var userRoles = await db.Set<TUserRole>()
             .Where(a => a.UserId.Equals(user.Id) && roleIds.Contains(a.RoleId))
             .ToListAsync(cancellation);
        if (userRoles.Count == 0) return;
        db.RemoveRange(userRoles);
        if (await db.SaveChangesAsync(cancellation) > 0)
        {
            cache.Remove(user.Id);
        }
    }

    public virtual async Task<bool> DeleteAsync(TUser user, CancellationToken cancellation)
    {
        if (UserNames.Admin.Equals(user.Username, StringComparison.Ordinal))
        {
            throw FriendlyException.BadRequest("不能删除管理员用户");
        }
        if (db.Entry(user).State != EntityState.Deleted)
        {
            db.Entry(user).State = EntityState.Deleted;
        }
        if (await db.SaveChangesAsync(cancellation) > 0)
        {
            cache.Remove(user.Id);
            return true;
        }
        return false;
    }

    public virtual async Task SetStatusAsync(TUser user, bool isEnabled, CancellationToken cancellation)
    {
        if (db.Entry(user).State == EntityState.Detached)
        {
            db.Attach(user);
        }
        user.IsEnabled = isEnabled;
        if (await db.SaveChangesAsync(cancellation) > 0)
        {
            cache.Remove(user.Id);
        }
    }

    public virtual async Task<TUser?> FindByNameAsync(string username, CancellationToken cancellation)
    {
        return await db.Set<TUser>().FirstOrDefaultAsync(a => a.Username == username, cancellation);
    }

    public virtual async Task<TUser?> FindByIdAsync(long id, CancellationToken cancellation)
    {
        return await db.Set<TUser>().FirstOrDefaultAsync(a => a.Id.Equals(id), cancellation);
    }

    public virtual async Task<string?> GetNameAsync(long id, CancellationToken cancellation)
    {
        return await db.Set<TUser>().Where(a => a.Id.Equals(id)).Select(a => a.Name).FirstOrDefaultAsync(cancellation);
    }

    public virtual async Task<TUser?> FindByPasswordAsync(string username, string password, CancellationToken cancellation)
    {
        var user = await db.Set<TUser>().FirstOrDefaultAsync(a => a.Username == username && a.IsEnabled, cancellation);
        return user?.ValidatePassword(password) == true ? user : null;
    }

    public virtual async Task<string[]> GetRolesAsync(long userId, CancellationToken cancellation)
    {
        return await db.Set<TUserRole>().AsNoTracking()
            .Where(a => a.UserId == userId)
            .Select(a => a.Role.Code)
            .ToArrayAsync(cancellation);
    }

    public virtual async Task SetOrganizationAsync(long id, long organizationId, CancellationToken cancellation)
    {
        var user = await db.Set<TUser>()
            .FirstOrDefaultAsync(a => a.Id == id, cancellation)
            ?? throw FriendlyException.BadRequest("用户不存在");
        if (organizationId <= 0)
        {
            user.OrganizationId = null;
        }
        else
        {
            if (user.OrganizationId == organizationId) return;
            if (!await db.Set<OrganizationUnit>().AsNoTracking().AnyAsync(a => a.Id == organizationId, cancellation))
            {
                throw FriendlyException.BadRequest("组织机构不存在");
            }

            user.OrganizationId = organizationId;
        }
        if (await db.SaveChangesAsync(cancellation) > 0)
        {
            cache.Remove(user.Id);
        }
    }

    public async Task<ApiResponse> ChangePasswordAsync(long id, string oldPassword, string newPassword, CancellationToken cancellation)
    {
        var user = await db.Set<TUser>().FirstOrDefaultAsync(a => a.Id.Equals(id), cancellation);
        if (user is null)
        {
            return ApiResponse.BadRequest("用户不存在");
        }

        if (!user.ValidatePassword(oldPassword))
        {
            return ApiResponse.BadRequest("原密码不正确");
        }

        user.SetPassword(newPassword);
        await db.SaveChangesAsync(cancellation);
        return ApiResponse.Ok;
    }
}